package cn.iocoder.yudao.module.engine.service.core.pipeline;

import cn.iocoder.yudao.module.engine.model.context.ControlEngineContext;
import io.netty.util.concurrent.*;

import java.security.InvalidParameterException;

public class FilterPipeline {
    private FilterContext head;
    private FilterContext tail;
    private int state = 0;// 0 running 1 close

    public FilterPipeline() {
    }

    public void init(FilterContext head, FilterContext tail) {
        this.head = head;
        this.tail = tail;
        head.next = tail;
        tail.prev = head;
        head.setAdded();
        tail.setAdded();
    }

    public void close() {
        state = 1;
    }

    private static void safeExecute(EventExecutor executor, Runnable runnable, Promise promise) {
        try {
            executor.execute(runnable);
        } catch (Throwable cause) {
            promise.tryFailure(cause);
        }
    }

    public boolean isClose() {
        return state == 1;
    }

    public void write(ControlEngineContext obj) {
        FilterContext.invokeWrite(tail, obj);
    }

    public void fireRead(ControlEngineContext obj) {
        FilterContext.invokeRead(head, obj);
    }

    public void addLast(String name, IFilter filter) {
        name = filterName(name, filter);
        final FilterContext newCtx = newContext(name, filter);
        addBefore(tail, newCtx);
        this.handleAddContext(newCtx);
    }

    public void addFirst(String name, IFilter filter) {
        name = filterName(name, filter);
        final FilterContext newCtx = newContext(name, filter);
        addAfter(head, newCtx);
        this.handleAddContext(newCtx);
    }


    private String filterName(String name, IFilter filter) {
        if (name == null) {
            throw new NullPointerException("name is null");
        }
        checkDuplicateName(name);
        return name;
    }

    private void checkDuplicateName(String name) {
        if (getContext(name) != null) {
            throw new IllegalArgumentException("Duplicate converter name: " + name);
        }
    }

    public FilterContext getContext(String name) {
        FilterContext context = head.next;
        while (context != tail) {
            if (context.getName().equals(name)) {
                return context;
            }
            context = context.next;
        }
        return null;
    }

    private void handleAddContext(final FilterContext ctx) {
        try {
            ctx.getFilter().filterAdded(ctx);
            ctx.setAdded();
        } catch (Throwable t) {
            boolean removed = false;
            try {
                remove(ctx);
                try {
                    ctx.getFilter().filterRemoved(ctx);
                } finally {
                    ctx.setRemoved();
                }
                removed = true;
            } catch (Throwable t2) {
            }

            if (removed) {
                throw new InvalidParameterException("add ctx fail and removed");
            } else {
                throw new InvalidParameterException("remove ctx fail");
            }
        }
    }

    private static void remove(FilterContext ctx) {
        FilterContext prev = ctx.prev;
        FilterContext next = ctx.next;
        prev.next = next;
        next.prev = prev;
    }

    private static void addAfter(FilterContext ctx, FilterContext newCtx) {
        newCtx.prev = ctx;
        newCtx.next = ctx.next;
        ctx.next.prev = newCtx;
        ctx.next = newCtx;
    }

    private static void addBefore(FilterContext ctx, FilterContext newCtx) {
        newCtx.prev = ctx.prev;
        newCtx.next = ctx;
        ctx.prev.next = newCtx;
        ctx.prev = newCtx;
    }

    private FilterContext newContext(String name, IFilter filter) {
        return new FilterContext(this, filter, name);
    }

}
